package org.nlp2rdf.navigator.benchmark;

import com.google.common.collect.Sets;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import org.aksw.commons.util.SerializationUtils;
import org.aksw.commons.util.experiments.ExperimentStatisticsCollector;
import org.aksw.commons.util.random.RandomUtils;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Options;
import org.dllearner.core.EvaluatedDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.*;
import java.net.URLEncoder;
import java.util.*;

/**
 * Created by Claus Stadler
 * Date: Oct 21, 2010
 * Time: 5:36:39 PM
 */
public class HistorySimulationBenchmark {
    private static final Logger logger = LoggerFactory.getLogger(ClausBenchmark.class);

    private static Options cliOptions = new Options()
        .addOption("c", "categoryFile", true, "File containing a list of category names");

    static ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {
            "org/nlp2rdf/navigator/xml/benchmark.xml",
            "navigator_dbpedia.xml" });



    public static void validatePool() {
        BenchmarkContext con = new BenchmarkContext(context);

        Random random = new Random(0l);

        Resource category = ResourceFactory.createResource("http://dbpedia.org/resource/Category:American_people_of_Danish_descent");

        Set<Resource> positives = MySparqlTasks.getTypedDBpediaCategoryMembers(con.getSparqlEndpoint(), con.getGraphNames(), category);
        
        Sample<Resource> pool = PoolUtils.generateNavigationPool(con.getSparqlEndpoint(), con.getGraphNames(), category, positives, random);


        System.out.println("Heres my result:");
        System.out.println(Sets.intersection(pool.getPositives(), pool.getNegatives()));
    }


    public static void main(String[] args)
        throws Exception
    {
        CommandLineParser cliParser = new GnuParser();
        CommandLine commandLine = cliParser.parse(cliOptions, args);

        List<Resource> categories = BenchmarkUtils.readResources(commandLine.getOptionValue("c"));
        //simulateNavigation(categories);

        //List<Resource> categories = Collections.emptyList();
        //validatePool();

        BenchmarkContext con = new BenchmarkContext(context);
        Resource category = ResourceFactory.createResource("http://dbpedia.org/resource/Category:American_people_of_Danish_descent");

        Set<Resource> positives = MySparqlTasks.getTypedDBpediaCategoryMembers(con.getSparqlEndpoint(), con.getGraphNames(), category);

        Sample<Resource> sample = PoolUtils.generateKeywordSearchPool(con.getSparqlEndpoint(), con.getGraphNames(), category.toString(), positives);

        System.out.println(sample);
    }


    public static void simulateNavigation(List<Resource> categories) //ApplicationContext context)
         throws Exception
     {

         // Actually here should be something like (BenchmarkConfig)context.get("benchmarkBean")
         BenchmarkContext con = new BenchmarkContext(context);
         String basePath = (String)context.getBean("basePath");
         int numPhases = 5;
         String experimentName = "Quick_Max3FoldsAndMax5Phases_TenFoldCV_HistoryNavigation";

         Random random = new Random(0l);

         /*
         Multimap<Integer, Resource> sizeToCategory = MySparqlTasks.getCategoriesWithTypedMembersAndWithoutNumbers(con.getSparqlEndpoint(), con.getGraphNames());
         Collection<Resource> cats = sizeToCategory.get(100);
         List<Resource> c = new ArrayList<Resource>(cats);
         Collections.sort(c, new ResourceComparator());
         for(Resource r : c) {
             System.out.println(r);
         }

         System.exit(0);
        */


         //Multimap<Integer, Resource> sizeToCategory = MySparqlTasks.getCategoriesWithTypedMembersAndWithoutNumbers(con.getSparqlEndpoint(), con.getGraphNames());

         String experimentPath = basePath + "/" + experimentName;


         for(Resource category : categories) {
             logger.info("Entering category " + category);
             
         //logger.info("Categories with 100 typed instances: " + SampleStats.toStringWithSize(sizeToCategory.get(100)));

         //for(Map.Entry<Integer, Resource> entry : sizeToCategory.entries()) {
/*
             if(entry.getKey() < 100) {
                 continue;
             }

             if(entry.getKey() > 150)
                System.exit(0);

             System.out.println("<" + entry.getValue() + ">");

         }

         while(true) {
             Map.Entry<Integer, Resource> entry = null;
             Resource category = entry.getValue();

                 if(entry.getKey() != 100) {
                     logger.debug("Skipping " + category);
                     continue;
                 }
         */

             try {


                 String categoryPath = experimentPath + "/" + URLEncoder.encode(category.toString(), "UTF-8");


                 Sample<Resource> all = Sample.create(
                         MySparqlTasks.getDBpediaCategoryMembers(con.getSparqlEndpoint(), con.getGraphNames(), category),
                         null);

                 SerializationUtils.serializeXml(Sample.createCopy(all), new File(categoryPath + "/all.xml"), true);

                 Sample<Resource> allTyped = Sample.create(
                         MySparqlTasks.getTypedDBpediaCategoryMembers(con.getSparqlEndpoint(), con.getGraphNames(), category),
                         null);

                 SerializationUtils.serializeXml(Sample.createCopy(all), new File(categoryPath + "/allTyped.xml"), true);

                 logger.info("Typed Members: " + SampleStats.toStringWithSize(allTyped.getPositives()));


                 // Fixme: It seems as if there not all positives come back again
                 // Try to retrieve enough seed negatives
                 //pool.getPositives().addAll(allTyped.getPositives());
                 Sample<Resource> pool =  PoolUtils.generateNavigationPool(con.getSparqlEndpoint(), con.getGraphNames(), category, allTyped.getPositives(), random);       
                 /*
                 Sample<Resource> pool = Sample.create();
                 Set<Resource> remaining = new HashSet<Resource>(allTyped.getPositives());
                 int numPoolIterations = 0;
                 while(!remaining.isEmpty() && (pool.getPositives().size() < 100 && pool.getNegatives().size() < 100)) {
                     ++numPoolIterations;

                     // FIXME May result in endless loop
                     logger.debug("Constructing pool: remaining/+/- = " + Joiner.on("/").join(remaining.size(), pool.getPositives().size(), pool.getNegatives().size()));
                     Resource navigationSeed = RandomUtils.randomItem(remaining, random);

                     Sample<Resource> tmpPool = MySparqlTasks.getNavigationExamples(con.getSparqlEndpoint(), con.getGraphNames(), navigationSeed, category, 1);

                     // Just to make sure the pool is actually correct
                     Sample<Resource> tmpPool2 = Sample.create(
                             Sets.intersection(allTyped.getPositives(), tmpPool.getPositives()),
                             tmpPool.getNegatives());

                     remaining.remove(navigationSeed);
                     //remaining.removeAll(tmpPool.getPositives());
                     pool.addAll(tmpPool2);

                 }
                 logger.info("poolIterations: " + numPoolIterations);
                 */
                 logger.info("poolPositives: " + SampleStats.toStringWithSize(pool.getPositives()));
                 logger.info("poolNegatives: " + SampleStats.toStringWithSize(pool.getNegatives()));

                 SerializationUtils.serializeXml(Sample.createCopy(pool), new File(categoryPath + "/navigationPool.xml"), true);


                 ExperimentStatisticsCollector collector = new ExperimentStatisticsCollector(experimentName, numPhases);

                 KFoldCollection<Resource> kFolds = KFoldCollection.create(10, pool, 0.5f, random);

                 int foldId = 0;
                 for(KFoldContext<Resource> kFold : kFolds) {
                     ++foldId;

                     if(foldId >= 3)
                         break;

                     logger.info("  Entering fold " + foldId);
                     logger.info("    testPositives:  " + SampleStats.toStringWithSize(kFold.getTestSample().getPositives()));
                     logger.info("    testNegatives:  " + SampleStats.toStringWithSize(kFold.getTestSample().getNegatives()));
                     logger.info("    foldPositives:  " + SampleStats.toStringWithSize(kFold.getTrainSample().getPositives()));
                     logger.info("    foldNegatives:  " + SampleStats.toStringWithSize(kFold.getTrainSample().getNegatives()));

                     String foldPath = categoryPath + foldId;

                     Sample<Resource> init = new Sample<Resource>(
                             RandomUtils.randomSampleSet(kFold.getTrainSample().getPositives(), 5, random),
                             RandomUtils.randomSampleSet(kFold.getTrainSample().getNegatives(), 5, random));

                     PhaseIterator phaseIterator = new PhaseIterator(allTyped, kFold.getTrainSample(), init, random, con.getLearner(), con.getClassifier());

                     for(int i = 0; i < numPhases; ++i) {
                         logger.info("    Entering phase " + (i + 1));

                         String phasePath = foldPath + "-" + i + "-";

                         logger.info("      trainPositives: " + SampleStats.toStringWithSize(phaseIterator.getNext().getPositives()));
                         logger.info("      trainNegatives: " + SampleStats.toStringWithSize(phaseIterator.getNext().getNegatives()));

                         PhaseContext<Resource, EvaluatedDescription> phaseResult = phaseIterator.next();

                         SampleStats<Resource> allStats = SampleStats.create(all, phaseResult.getExamples());
                         SampleStats<Resource> allTypedStats = SampleStats.create(allTyped, phaseResult.getExamples());

                         SampleStats<Resource> testAllStats = SampleStats.create(kFold.getTestSample(), phaseResult.getExamples(), all);
                         SampleStats<Resource> testAllTypedStats = SampleStats.create(kFold.getTestSample(), phaseResult.getExamples(), allTyped);


                         BenchmarkUtils.add(collector, i, "all", allStats);
                         BenchmarkUtils.add(collector, i, "allTyped", allTypedStats);
                         BenchmarkUtils.add(collector, i, "testAll", testAllStats);
                         BenchmarkUtils.add(collector, i, "typedAllTyped", testAllTypedStats);

                         logger.info("      learned:        " + phaseResult.getLearnedDescription().getDescription().toKBSyntaxString());
                         logger.info("      #instances      " + con.getClassifier().countInstances(phaseResult.getLearnedDescription()));
                         logger.info("      accuracy:       " + phaseResult.getLearnedDescription().getAccuracy());
                         logger.info("      all:            " + allStats);
                         logger.info("      allTyped:       " + allTypedStats);
                         logger.info("      testAll:        " + testAllStats);
                         logger.info("      typedAllTyped:  " + testAllTypedStats);

                         SerializationUtils.serializeXml(phaseResult, new File(phasePath + "phaseResult.xml"), true);

                         String label = "BAD";
                         if(allTypedStats.getFMeasure() >= 0.5 && phaseResult.getLearnedDescription().getAccuracy() > 0.99) {
                             label = "GOOD";
                         }

                         logger.info("      [" + label + "] " + category + " --- " + allTypedStats);



                         if(label.equals("GOOD")) {
                             foldId = 999;
                             break;
                         }


                     }

                     BenchmarkUtils.writeOutStatistics(categoryPath + "table", collector.getTable());
                 }
             } catch(Exception e) {
                 logger.error("Error while processing " + category, e);
             }
         }
     }
}
